// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2012-2017, Samsung Electronics. All rights reserved.
// Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.

#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>

#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>

#include <video/mipi_display.h>

#define to_s6e3fa7_drv(drm_panel) \
	container_of(drm_panel, struct s6e3fa7_drv, panel)

#define LEVEL1_ON	0xf0, 0x5a, 0x5a
#define LEVEL1_OFF	0xf0, 0xa5, 0xa5
#define LUMINANCE_MAX	    74
#define GRAY_SCALE_MAX	    256
#define RGB_COMPENSATION    9
#define FP_SHIFT	    22
#define VREG0_REF_6P5 ((int) (6.5 * (2 << 21)))
#define GAMMA_CMD_LEN	    34
#define NUM_VREGS	    3

struct s6e3fa7_drv {
	struct device *dev;
	struct drm_panel panel;
	struct gpio_desc *reset;
	struct mipi_dsi_device *dsi;
	struct regulator_bulk_data vregs[NUM_VREGS];
	bool bl_inited, vregs_enabled;

	/* calibration values */
	u8 elvss_params[2], irc_params[2];
	char gamma_cmds[LUMINANCE_MAX][GAMMA_CMD_LEN];
};

enum { VT, V1, V7, V11, V23, V35, V51, V87, V151, V203, V255, V_MAX };

static const u8 level1_on[] = { 0xf0, 0x5a, 0x5a };
static const u8 level1_off[] = { 0xf0, 0xa5, 0xa5 };
static const u8 gamma_update_cmd[] = { 0xf7, 0x03 };

static const signed char red_offsets[LUMINANCE_MAX][RGB_COMPENSATION] = {
/*	{  R255,  R203,  R151,  R87,  R51,  R35,  R23,  R11,   R7  },	*/
	{    -5,    -2,    -5,  -14,  -22,  -19,  -22,  -23,  -13  },
	{    -3,    -2,    -3,  -11,  -21,  -19,  -24,  -25,  -13  },
	{    -2,    -2,    -3,  -11,  -20,  -19,  -22,  -24,  -13  },
	{    -1,    -2,    -3,  -10,  -20,  -20,  -21,  -24,  -13  },
	{     0,    -1,    -3,   -9,  -20,  -20,  -22,  -24,  -13  },
	{     0,    -1,    -3,   -7,  -20,  -18,  -22,  -25,  -13  },
	{     0,    -1,    -3,   -7,  -18,  -15,  -20,  -20,  -10  },
	{     0,    -1,    -3,   -6,  -16,  -15,  -17,  -20,  -10  },
	{     0,    -1,    -3,   -6,  -14,  -11,  -16,  -16,   -9  },
	{     0,     0,    -3,   -4,  -14,   -9,  -16,  -15,   -9  },
	{     0,     0,    -3,   -4,  -13,  -10,  -16,  -11,   -8  },
	{     0,     0,    -3,   -4,  -13,  -10,  -14,  -11,   -8  },
	{     0,     0,    -3,   -4,  -13,  -10,  -14,   -9,   -8  },
	{     1,     0,    -3,   -4,  -13,  -10,  -14,   -9,   -8  },
	{     1,     0,    -2,   -4,  -13,  -10,  -12,   -7,   -7  },
	{     1,     0,    -2,   -4,  -12,  -10,  -13,   -9,   -7  },
	{     1,     0,    -2,   -3,  -11,  -10,  -13,  -10,   -7  },
	{     0,     0,    -2,   -3,  -11,   -9,  -13,  -12,   -7  },
	{     1,     0,    -2,   -3,   -7,  -11,  -11,  -15,   -7  },
	{     1,     0,    -2,   -3,   -7,  -11,  -12,  -11,   -7  },
	{     1,     0,    -2,   -2,   -7,  -10,  -11,  -12,   -7  },
	{     1,     0,    -1,   -2,   -7,   -7,  -10,  -13,   -7  },
	{     1,     0,    -1,   -2,   -6,   -6,   -8,  -15,   -7  },
	{     1,     0,    -1,   -2,   -6,   -6,   -9,  -15,   -4  },
	{     1,     0,    -1,   -2,   -4,   -7,  -10,  -15,   -4  },
	{     1,     0,    -1,   -2,   -4,   -5,  -10,  -14,   -5  },
	{     1,     0,    -1,   -2,   -4,   -5,  -10,  -16,   -5  },
	{     1,     0,    -1,   -2,   -4,   -5,  -11,  -14,   -4  },
	{     1,     0,    -1,   -2,   -5,   -5,  -11,  -14,   -4  },
	{     1,     0,    -1,   -2,   -5,   -4,  -11,  -15,   -5  },
	{     1,     0,    -1,   -2,   -2,   -3,   -9,  -15,   -5  },
	{     1,     0,    -1,   -2,   -2,   -3,   -7,  -13,   -3  },
	{     1,     0,    -1,   -2,   -2,   -3,   -7,  -11,   -3  },
	{     1,     0,    -1,   -2,   -2,   -3,   -7,  -11,   -5  },
	{     1,     0,    -1,   -1,   -3,   -4,   -6,  -15,   -3  },
	{     1,     0,    -1,   -2,   -2,   -2,   -7,  -15,   -5  },
	{     0,     0,    -1,   -1,   -2,   -3,   -9,  -15,   -4  },
	{     1,     0,    -1,   -1,   -2,   -4,   -5,  -16,   -4  },
	{     1,     0,    -2,   -1,   -1,   -3,   -8,  -18,   -2  },
	{     1,     0,    -1,   -1,   -2,   -5,   -6,  -18,   -1  },
	{     1,     0,    -2,    1,   -2,   -4,   -4,  -14,   -6  },
	{     0,     1,     0,   -1,   -3,   -1,   -4,  -13,   -8  },
	{     1,     0,    -1,    0,   -1,   -5,   -3,  -13,   -8  },
	{     0,     0,     0,    0,   -2,   -1,   -6,  -16,   -5  },
	{     1,     0,    -1,    0,   -1,   -2,   -4,  -16,   -5  },
	{     0,     1,     0,   -1,   -1,   -1,   -2,  -16,   -6  },
	{     0,     0,    -1,   -1,   -2,   -1,   -3,  -13,   -6  },
	{     1,     1,    -1,   -2,   -2,    0,   -3,  -14,   -6  },
	{     0,     0,     0,    0,   -3,    0,   -5,  -15,   -8  },
	{     0,     1,    -1,    0,   -2,   -1,   -2,  -15,  -11  },
	{    -1,     0,     0,   -1,    0,    1,   -6,  -12,  -11  },
	{    -1,     0,    -1,    1,   -1,   -1,   -4,  -11,  -11  },
	{    -1,     1,     0,    0,    0,    0,   -1,   -8,  -11  },
	{     0,     1,     1,   -1,   -2,   -1,    0,   -8,  -10  },
	{     0,     1,     0,   -1,   -1,    0,    0,   -7,  -13  },
	{     0,     1,     0,   -1,   -1,   -1,   -1,   -9,   -5  },
	{     0,     0,     0,    0,    0,    0,   -2,   -8,   -3  },
	{     0,     0,     0,    0,    0,    0,    1,   -7,   -4  },
	{    -1,     0,     1,    1,    0,    0,   -2,   -5,   -4  },
	{     0,     0,     0,    0,   -1,   -1,    0,   -5,   -5  },
	{     0,     0,     0,    0,    1,   -1,    0,   -7,   -7  },
	{     0,     0,     0,    0,    0,    1,   -2,   -2,   -8  },
	{     1,     0,     0,   -1,    0,    1,    1,   -2,   -9  },
	{     0,     0,     0,    1,    0,    0,   -3,    0,   -5  },
	{     0,     0,     1,    0,    0,    0,   -3,    0,    0  },
	{     1,    -1,     0,    0,    1,    2,   -4,    0,   -2  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   -1  },
	{     0,     0,     1,   -1,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
};

static const signed char green_offsets[LUMINANCE_MAX][RGB_COMPENSATION] = {
/*	{  G255,  G203,  G151,  G87,  G51,  G35,  G23,  G11,  G7  },	*/
	{     0,     1,     1,    1,   -1,   -1,   -8,   -3,  -6  },
	{     0,     1,     0,    0,   -4,   -3,   -8,   -5,  -6  },
	{     0,     1,    -1,   -1,   -2,   -3,   -9,   -7,  -6  },
	{     0,     0,    -1,    0,   -5,   -6,   -2,   -7,  -6  },
	{     0,     0,    -1,    0,   -5,   -6,   -5,   -7,  -6  },
	{     0,     0,    -1,   -1,   -5,   -6,   -7,   -8,  -6  },
	{     0,     0,    -1,   -1,   -5,   -2,   -5,   -8,  -6  },
	{     0,     0,    -1,   -1,   -4,   -2,   -4,   -8,  -6  },
	{     0,     0,    -1,   -1,   -3,    0,   -3,   -6,  -5  },
	{     0,     1,    -1,   -1,   -2,    2,   -4,   -5,  -5  },
	{     0,     1,    -1,   -1,   -1,    1,   -4,    0,  -4  },
	{     0,     1,    -1,   -1,   -1,    0,   -3,    0,  -4  },
	{     0,     1,    -1,   -1,   -2,    0,   -3,    0,  -4  },
	{     0,     1,    -1,   -1,   -2,    0,   -3,    0,  -4  },
	{     0,     1,     0,   -1,   -2,    0,    0,    2,  -3  },
	{     0,     1,     0,   -1,   -2,   -1,   -1,    2,  -5  },
	{     0,     1,     0,   -1,   -1,   -1,   -2,    1,  -5  },
	{     0,     1,     0,   -1,   -2,    1,   -4,    0,  -5  },
	{     0,     1,    -1,    0,    1,   -3,    0,   -3,  -5  },
	{     0,     1,    -1,    0,    0,   -3,   -1,    2,  -5  },
	{     0,     1,    -1,    0,    0,   -1,    0,    1,  -5  },
	{     0,     1,     0,    0,    0,    1,    1,    0,  -5  },
	{     0,     1,     0,    0,    0,    3,    2,    1,  -5  },
	{     0,     1,     0,   -1,    0,    2,    1,    1,   1  },
	{     0,     1,     0,   -1,    2,    0,    0,   -1,   1  },
	{     0,     1,     0,   -1,    2,    2,    1,    0,   0  },
	{     0,     1,     0,   -1,    1,    2,    0,   -2,   0  },
	{     0,     1,     0,   -1,    0,    2,   -1,    2,   1  },
	{     0,     1,     0,   -1,   -1,    1,   -1,    2,   1  },
	{     0,     1,     0,   -1,   -1,    1,   -2,    0,   0  },
	{     0,     1,     0,   -2,    2,    2,   -2,    0,  -1  },
	{     0,     1,     0,   -2,    1,    1,    1,    2,   1  },
	{     0,     1,     0,   -2,    1,    1,    1,    4,   1  },
	{     0,     1,     0,   -2,    1,    0,    1,    3,   0  },
	{     0,     1,     0,   -2,    1,    0,    0,    1,   0  },
	{     0,     1,     0,   -2,    0,    1,   -1,    0,  -1  },
	{     0,     0,     0,   -1,    0,   -1,   -3,    0,  -2  },
	{     0,     1,     0,   -1,    0,   -2,    2,   -1,  -1  },
	{     0,     0,    -1,   -1,    1,    0,   -2,   -3,   4  },
	{     0,     1,     0,   -1,    0,   -3,    0,   -2,   4  },
	{     0,     1,     0,    0,    1,   -2,    1,    1,  -2  },
	{     0,     1,     0,   -1,    0,    1,    1,    1,  -2  },
	{     0,     0,     0,    0,    1,   -1,    1,    2,  -3  },
	{     0,     0,     0,    0,    1,    1,   -2,   -2,   3  },
	{     0,     0,     0,    0,    0,    1,   -1,   -1,   2  },
	{     0,     1,     0,   -1,    0,    2,    0,   -2,   2  },
	{     0,     0,     0,   -1,   -2,    0,    1,    1,   2  },
	{     0,     0,     0,   -1,   -1,    2,    2,    0,   1  },
	{     0,     0,     0,    0,   -1,    1,   -2,   -2,  -1  },
	{     0,     0,     0,    0,   -1,    0,    1,   -4,  -1  },
	{     0,     0,     0,    0,    0,    2,   -3,    0,  -1  },
	{     0,    -1,     0,    1,   -1,    1,   -1,    0,   0  },
	{     0,     0,     1,    0,    1,    1,    2,    2,  -1  },
	{     0,     0,     1,    0,    0,    0,    3,    3,  -1  },
	{     0,     0,     0,    0,    1,    1,    2,    1,  -2  },
	{     0,     0,     0,    0,    1,   -1,    1,    0,   2  },
	{     0,     0,     0,    0,    1,    0,    1,    2,   2  },
	{     0,     0,     0,    0,    0,    1,    3,    2,   1  },
	{     0,     0,     0,    1,    0,    1,   -1,    3,   2  },
	{     0,     0,     0,    0,    0,    1,    2,    2,   0  },
	{     0,     0,     0,    0,    0,    2,    1,    1,  -2  },
	{     0,     0,     0,    0,    0,    1,   -1,    2,  -3  },
	{     0,     0,     0,    0,    0,    1,    2,    1,  -4  },
	{     0,     0,     0,    1,    0,    0,   -2,    3,   2  },
	{     0,     0,     1,    0,    0,    1,   -2,    2,   2  },
	{     0,     0,     0,    0,    1,    1,   -3,    2,   2  },
	{     0,     0,     0,    0,    0,    0,    0,    1,   2  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   2  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   2  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,   0  },
};

static const signed char blue_offsets[LUMINANCE_MAX][RGB_COMPENSATION] = {
/*	{  B255,  B203,  B151,  B87,  B51,  B35,  B23,  B11,   B7  },	*/
	{    -2,    -2,    -2,   -7,  -10,   -6,  -10,   -4,   -5  },
	{    -1,    -1,    -1,   -7,  -12,   -7,  -11,   -8,   -8  },
	{    -1,    -1,    -1,   -7,  -12,   -7,  -10,  -11,   -8  },
	{    -1,    -1,    -1,   -7,  -15,   -9,   -7,  -11,   -8  },
	{     0,    -1,    -1,   -6,  -16,   -9,   -9,  -11,   -8  },
	{     0,    -1,    -1,   -5,  -16,  -10,  -10,  -12,   -8  },
	{     0,    -1,    -1,   -5,  -16,   -7,  -10,  -12,   -8  },
	{     0,    -1,    -1,   -5,  -14,   -7,  -10,  -12,   -8  },
	{     0,    -1,    -1,   -5,  -13,   -6,   -9,  -12,   -8  },
	{    -1,     0,    -1,   -3,  -14,   -4,  -10,  -11,   -8  },
	{    -1,     0,    -1,   -3,  -13,   -6,  -10,   -8,   -6  },
	{    -1,     0,    -1,   -3,  -13,   -7,  -10,   -8,   -6  },
	{    -1,     0,    -1,   -3,  -13,   -7,  -10,   -8,   -6  },
	{    -1,     0,    -1,   -3,  -13,   -7,  -11,   -8,   -6  },
	{    -1,     0,    -1,   -3,  -13,   -7,  -10,   -8,   -5  },
	{    -1,     0,    -1,   -3,  -12,   -7,  -11,   -9,   -7  },
	{    -1,     0,    -1,   -2,  -12,   -7,  -11,   -9,   -7  },
	{    -1,     0,    -1,   -2,  -11,   -7,  -11,  -11,   -7  },
	{    -1,     0,    -1,   -2,   -9,   -9,   -9,  -14,   -7  },
	{    -1,     0,    -1,   -2,   -9,   -9,  -10,  -10,   -7  },
	{    -1,     0,    -1,   -1,   -9,   -8,   -9,  -12,   -7  },
	{    -1,     0,     0,   -2,   -9,   -5,   -8,  -12,   -7  },
	{    -1,     0,     0,   -2,   -8,   -4,   -7,  -12,   -7  },
	{    -1,     0,     0,   -2,   -8,   -4,   -8,  -12,   -4  },
	{    -1,     0,     0,   -2,   -7,   -5,   -8,  -12,   -4  },
	{    -1,     0,     0,   -2,   -7,   -2,   -8,  -13,   -4  },
	{    -1,     0,     0,   -2,   -7,   -2,   -8,  -15,   -4  },
	{    -1,     0,     0,   -2,   -7,   -2,   -9,  -13,   -3  },
	{    -1,     0,     0,   -2,   -8,   -2,   -8,  -13,   -4  },
	{    -1,     0,     0,   -2,   -8,   -2,   -8,  -14,   -5  },
	{    -1,     0,     0,   -2,   -5,   -2,   -7,  -14,   -5  },
	{    -1,     0,     0,   -2,   -5,   -2,   -5,  -14,   -2  },
	{    -1,     0,     0,   -2,   -5,   -2,   -5,  -12,   -2  },
	{    -1,     0,     0,   -2,   -5,   -2,   -5,  -12,   -4  },
	{    -1,     0,     0,   -2,   -5,   -2,   -5,  -14,   -4  },
	{    -1,     0,     0,   -2,   -5,   -1,   -5,  -14,   -4  },
	{    -1,    -1,     0,   -1,   -5,   -2,   -7,  -14,   -5  },
	{    -1,     1,     0,   -1,   -5,   -2,   -4,  -15,   -5  },
	{    -1,     0,     0,   -1,   -4,   -1,   -7,  -15,   -3  },
	{    -1,     0,     1,   -1,   -5,   -3,   -5,  -15,   -3  },
	{    -1,     0,     0,    1,   -5,   -2,   -3,  -12,   -7  },
	{    -1,     0,     1,   -1,   -4,    0,   -3,  -12,   -8  },
	{    -1,     0,     0,    1,   -4,   -2,   -3,  -11,   -9  },
	{     0,    -1,     1,    0,   -4,   -1,   -5,  -14,   -4  },
	{     0,     0,     0,    0,   -4,    0,   -5,  -12,   -5  },
	{    -1,     0,     0,   -1,   -2,    0,   -2,  -13,   -6  },
	{     0,    -1,     0,    1,   -5,   -1,   -2,  -11,   -6  },
	{     0,     0,     0,    0,   -3,    0,   -1,  -12,   -7  },
	{     0,    -1,     0,    1,   -4,    0,   -4,  -12,   -9  },
	{     0,     0,     0,    1,   -2,   -1,   -2,  -14,  -11  },
	{    -1,    -1,    -1,    0,   -1,    1,   -5,  -11,  -11  },
	{    -1,    -1,    -1,    1,   -2,   -1,   -4,  -10,  -10  },
	{    -1,     0,     1,    1,   -1,    0,   -2,   -6,  -11  },
	{     0,     0,     1,    1,   -2,   -1,    0,   -7,  -10  },
	{     0,     0,     0,    1,   -1,    0,   -1,   -7,  -11  },
	{     0,     0,     0,    1,   -1,   -1,   -2,   -8,   -4  },
	{     0,     0,     0,    1,   -1,    0,   -1,   -7,   -4  },
	{     0,    -1,     0,    1,   -1,    1,    1,   -4,   -5  },
	{    -1,     0,     0,    1,    0,    0,   -2,   -4,   -5  },
	{     0,     0,     0,    1,   -2,    0,    0,   -4,   -3  },
	{    -1,     0,     0,    1,    0,    0,    0,   -6,   -6  },
	{    -1,     0,     0,    1,    0,    1,   -2,   -1,   -7  },
	{     0,     0,     0,    0,    0,    1,    1,   -1,   -8  },
	{    -1,     0,     0,    1,   -1,    0,   -2,    2,    0  },
	{     0,     0,     1,    0,    1,    0,   -3,    1,    1  },
	{     0,     0,     0,    0,    1,    2,   -4,    1,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    1  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    1  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
	{     0,     0,     0,    0,    0,    0,    0,    0,    0  },
};

static const unsigned char gamma_offsets[LUMINANCE_MAX][11] = {
	{ 0x00, 0x01, 0x22, 0x22, 0x26, 0x26, 0x2b, 0x37, 0x53, 0x6d, 0x86 },
	{ 0x00, 0x01, 0x1f, 0x21, 0x23, 0x24, 0x29, 0x34, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x1e, 0x1f, 0x21, 0x22, 0x27, 0x34, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x1d, 0x1d, 0x1f, 0x22, 0x27, 0x33, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x1c, 0x1c, 0x1e, 0x21, 0x25, 0x32, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x1b, 0x1b, 0x1d, 0x20, 0x25, 0x32, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x1a, 0x1a, 0x1c, 0x1f, 0x25, 0x32, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x19, 0x19, 0x1b, 0x1e, 0x24, 0x32, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x18, 0x18, 0x1a, 0x1d, 0x24, 0x32, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x17, 0x17, 0x19, 0x1c, 0x23, 0x31, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x16, 0x16, 0x19, 0x1c, 0x22, 0x31, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x16, 0x16, 0x19, 0x1c, 0x22, 0x31, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x16, 0x16, 0x19, 0x1c, 0x22, 0x31, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x16, 0x16, 0x19, 0x1c, 0x22, 0x31, 0x52, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x15, 0x15, 0x18, 0x1c, 0x22, 0x31, 0x51, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x15, 0x15, 0x18, 0x1c, 0x22, 0x31, 0x51, 0x6c, 0x86 },
	{ 0x00, 0x01, 0x14, 0x14, 0x17, 0x1b, 0x21, 0x31, 0x51, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x14, 0x14, 0x17, 0x1a, 0x21, 0x30, 0x51, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x14, 0x14, 0x16, 0x1a, 0x20, 0x30, 0x51, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x13, 0x13, 0x16, 0x1a, 0x20, 0x30, 0x51, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x12, 0x12, 0x15, 0x19, 0x20, 0x30, 0x51, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x11, 0x11, 0x14, 0x18, 0x1f, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x10, 0x10, 0x13, 0x17, 0x1f, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0f, 0x10, 0x13, 0x17, 0x1f, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0f, 0x10, 0x13, 0x17, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0e, 0x0f, 0x12, 0x16, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0e, 0x0f, 0x12, 0x16, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0d, 0x0e, 0x12, 0x16, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0d, 0x0e, 0x12, 0x16, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0d, 0x0e, 0x12, 0x16, 0x1e, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0c, 0x0d, 0x11, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0b, 0x0c, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x15, 0x1d, 0x2f, 0x50, 0x6b, 0x86 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x10, 0x16, 0x1e, 0x30, 0x53, 0x6f, 0x8a },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x11, 0x16, 0x1f, 0x32, 0x55, 0x72, 0x8e },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x11, 0x17, 0x1f, 0x33, 0x57, 0x75, 0x92 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x11, 0x17, 0x20, 0x34, 0x5a, 0x79, 0x96 },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x11, 0x17, 0x21, 0x36, 0x5c, 0x7c, 0x9a },
	{ 0x00, 0x01, 0x0a, 0x0b, 0x11, 0x18, 0x21, 0x37, 0x5f, 0x80, 0x9e },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x12, 0x18, 0x22, 0x39, 0x62, 0x83, 0xa3 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x12, 0x19, 0x23, 0x3b, 0x65, 0x88, 0xa8 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x12, 0x1a, 0x24, 0x3c, 0x68, 0x8a, 0xad },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x13, 0x1b, 0x26, 0x3f, 0x6c, 0x8e, 0xb2 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x13, 0x1b, 0x27, 0x41, 0x6e, 0x91, 0xb6 },
	{ 0x00, 0x01, 0x0b, 0x0d, 0x14, 0x1c, 0x28, 0x43, 0x72, 0x97, 0xbc },
	{ 0x00, 0x01, 0x0b, 0x0d, 0x14, 0x1c, 0x29, 0x45, 0x75, 0x9a, 0xc1 },
	{ 0x00, 0x01, 0x0b, 0x0d, 0x15, 0x1d, 0x2a, 0x47, 0x79, 0x9f, 0xc7 },
	{ 0x00, 0x01, 0x0b, 0x0d, 0x15, 0x1e, 0x2b, 0x48, 0x7d, 0xa4, 0xcd },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x15, 0x1e, 0x2b, 0x49, 0x7e, 0xa7, 0xd1 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x15, 0x1f, 0x2c, 0x4b, 0x81, 0xab, 0xd6 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x15, 0x1f, 0x2c, 0x4b, 0x81, 0xab, 0xd6 },
	{ 0x00, 0x01, 0x0a, 0x0c, 0x15, 0x1f, 0x2c, 0x4b, 0x81, 0xab, 0xd6 },
	{ 0x00, 0x01, 0x09, 0x0b, 0x15, 0x1f, 0x2c, 0x4b, 0x81, 0xab, 0xd6 },
	{ 0x00, 0x01, 0x08, 0x0b, 0x14, 0x1f, 0x2c, 0x4b, 0x81, 0xaa, 0xd6 },
	{ 0x00, 0x01, 0x08, 0x0b, 0x15, 0x1f, 0x2d, 0x4c, 0x83, 0xae, 0xda },
	{ 0x00, 0x01, 0x09, 0x0b, 0x15, 0x20, 0x2e, 0x4e, 0x86, 0xb3, 0xdf },
	{ 0x00, 0x01, 0x09, 0x0b, 0x16, 0x21, 0x2f, 0x50, 0x8a, 0xb6, 0xe4 },
	{ 0x00, 0x01, 0x09, 0x0b, 0x16, 0x21, 0x30, 0x51, 0x8c, 0xba, 0xe8 },
	{ 0x00, 0x01, 0x09, 0x0b, 0x16, 0x22, 0x31, 0x53, 0x8f, 0xbe, 0xed },
	{ 0x00, 0x01, 0x08, 0x0b, 0x17, 0x22, 0x31, 0x54, 0x92, 0xc2, 0xf2 },
	{ 0x00, 0x01, 0x08, 0x0b, 0x16, 0x23, 0x32, 0x55, 0x92, 0xc5, 0xf6 },
	{ 0x00, 0x01, 0x08, 0x0a, 0x17, 0x22, 0x31, 0x56, 0x95, 0xc6, 0xf8 },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x33, 0x57, 0x96, 0xc8, 0xfa },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x33, 0x57, 0x95, 0xc8, 0xfa },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x34, 0x57, 0x95, 0xc8, 0xfa },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x34, 0x57, 0x95, 0xc8, 0xfa },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x34, 0x57, 0x95, 0xc8, 0xfa },
	{ 0x00, 0x01, 0x07, 0x0b, 0x18, 0x24, 0x34, 0x58, 0x97, 0xca, 0xfc },
	{ 0x00, 0x01, 0x07, 0x0c, 0x18, 0x24, 0x34, 0x58, 0x98, 0xcb, 0xfd },
	{ 0x00, 0x01, 0x07, 0x0b, 0x17, 0x23, 0x33, 0x57, 0x97, 0xcb, 0xff },
};

static const struct brightness_level {
	u16 candela;
	u16 gamma_idx;
	u8 aid1;
	u8 aid2;
	u8 elvss2;
	u8 elvss3;
	u8 vint2;
	u8 irc25_27;
	u8 irc28_30;
	u8 irc31_33;
} brightness_settings[] = {
	{   3,   1, 0x08,  0x93,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x93,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x91,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x8f,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x89,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x87,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x85,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   3,   1, 0x08,  0x83,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x81,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x7f,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x7d,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x6e,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x6c,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x6a,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   4,   2, 0x08,  0x68,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x66,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x64,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x62,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x5c,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x5a,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x58,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   5,   3, 0x08,  0x56,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x54,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x52,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x50,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x4a,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x48,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x46,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   6,   4, 0x08,  0x44,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x42,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x40,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x37,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x35,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x33,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   7,   5, 0x08,  0x31,  0xcc, 0x04,  0x1c,  0x00,  0x00,  0x00 },
	{   8,   6, 0x08,  0x23,  0xcc, 0x05,  0x1c,  0x00,  0x00,  0x00 },
	{   8,   6, 0x08,  0x15,  0xcc, 0x05,  0x1c,  0x00,  0x00,  0x00 },
	{   9,   7, 0x08,  0x0b,  0xcc, 0x06,  0x1c,  0x00,  0x00,  0x00 },
	{   9,   7, 0x08,  0x01,  0xcc, 0x06,  0x1c,  0x00,  0x00,  0x00 },
	{  10,   8, 0x07,  0xf3,  0xcc, 0x07,  0x1d,  0x02,  0x01,  0x00 },
	{  10,   8, 0x07,  0xe4,  0xcc, 0x07,  0x1d,  0x02,  0x01,  0x00 },
	{  11,   9, 0x07,  0xda,  0xcc, 0x08,  0x1d,  0x02,  0x01,  0x00 },
	{  11,   9, 0x07,  0xd0,  0xcc, 0x08,  0x1d,  0x02,  0x01,  0x00 },
	{  12,  10, 0x07,  0xc2,  0xcc, 0x09,  0x1d,  0x02,  0x01,  0x01 },
	{  12,  10, 0x07,  0xb5,  0xcc, 0x09,  0x1d,  0x02,  0x01,  0x01 },
	{  13,  11, 0x07,  0xa6,  0xcc, 0x0a,  0x1d,  0x02,  0x01,  0x01 },
	{  13,  11, 0x07,  0x97,  0xcc, 0x0a,  0x1d,  0x02,  0x01,  0x01 },
	{  14,  12, 0x07,  0x8c,  0xcc, 0x0b,  0x1d,  0x03,  0x01,  0x00 },
	{  14,  12, 0x07,  0x81,  0xcc, 0x0b,  0x1d,  0x03,  0x01,  0x00 },
	{  15,  13, 0x07,  0x72,  0xcc, 0x0c,  0x1d,  0x03,  0x01,  0x01 },
	{  15,  13, 0x07,  0x64,  0xcc, 0x0c,  0x1d,  0x03,  0x01,  0x01 },
	{  16,  14, 0x07,  0x56,  0xcc, 0x0d,  0x1d,  0x03,  0x01,  0x01 },
	{  16,  14, 0x07,  0x47,  0xcc, 0x0d,  0x1d,  0x03,  0x01,  0x01 },
	{  17,  15, 0x07,  0x3d,  0xcc, 0x0d,  0x1d,  0x03,  0x01,  0x01 },
	{  17,  15, 0x07,  0x33,  0xcc, 0x0d,  0x1d,  0x03,  0x01,  0x01 },
	{  19,  16, 0x07,  0x1a,  0xcc, 0x0d,  0x1d,  0x03,  0x02,  0x01 },
	{  19,  16, 0x07,  0x02,  0xcc, 0x0d,  0x1d,  0x04,  0x01,  0x01 },
	{  20,  17, 0x06,  0xf1,  0xcc, 0x0d,  0x1e,  0x04,  0x01,  0x01 },
	{  21,  18, 0x06,  0xe2,  0xcc, 0x0d,  0x1e,  0x04,  0x01,  0x02 },
	{  21,  18, 0x06,  0xd4,  0xcc, 0x0d,  0x1e,  0x04,  0x01,  0x02 },
	{  22,  19, 0x06,  0xca,  0xcc, 0x0d,  0x1e,  0x04,  0x02,  0x01 },
	{  22,  19, 0x06,  0xc0,  0xcc, 0x0d,  0x1e,  0x04,  0x02,  0x01 },
	{  24,  20, 0x06,  0xa8,  0xcc, 0x0d,  0x1e,  0x04,  0x02,  0x01 },
	{  24,  20, 0x06,  0x90,  0xcc, 0x0d,  0x1e,  0x05,  0x01,  0x02 },
	{  25,  21, 0x06,  0x83,  0xcc, 0x0d,  0x1e,  0x05,  0x01,  0x02 },
	{  25,  21, 0x06,  0x75,  0xcc, 0x0d,  0x1e,  0x05,  0x01,  0x02 },
	{  27,  22, 0x06,  0x5d,  0xcc, 0x0d,  0x1e,  0x05,  0x02,  0x01 },
	{  27,  22, 0x06,  0x45,  0xcc, 0x0d,  0x1e,  0x05,  0x02,  0x02 },
	{  29,  23, 0x06,  0x2d,  0xcc, 0x0d,  0x1e,  0x05,  0x02,  0x02 },
	{  29,  23, 0x06,  0x14,  0xcc, 0x0d,  0x1e,  0x06,  0x01,  0x02 },
	{  30,  24, 0x05,  0xff,  0xcc, 0x0d,  0x1e,  0x06,  0x02,  0x02 },
	{  32,  25, 0x05,  0xe4,  0xcc, 0x0d,  0x1e,  0x06,  0x02,  0x02 },
	{  32,  25, 0x05,  0xc8,  0xcc, 0x0d,  0x1e,  0x06,  0x02,  0x02 },
	{  34,  26, 0x05,  0xaf,  0xcc, 0x0d,  0x1e,  0x06,  0x02,  0x02 },
	{  34,  26, 0x05,  0x96,  0xcc, 0x0d,  0x1e,  0x06,  0x03,  0x02 },
	{  37,  27, 0x05,  0x74,  0xcc, 0x0d,  0x1e,  0x07,  0x02,  0x02 },
	{  37,  27, 0x05,  0x52,  0xcc, 0x0d,  0x1e,  0x07,  0x02,  0x03 },
	{  39,  28, 0x05,  0x3a,  0xcc, 0x0d,  0x1e,  0x07,  0x03,  0x02 },
	{  39,  28, 0x05,  0x22,  0xcc, 0x0d,  0x1e,  0x07,  0x03,  0x02 },
	{  41,  29, 0x05,  0x09,  0xdc, 0x0d,  0x1e,  0x08,  0x02,  0x03 },
	{  41,  29, 0x04,  0xf0,  0xdc, 0x0d,  0x1e,  0x08,  0x02,  0x03 },
	{  44,  30, 0x04,  0xca,  0xdc, 0x0d,  0x1e,  0x08,  0x03,  0x03 },
	{  44,  30, 0x04,  0xa5,  0xdc, 0x0d,  0x1e,  0x08,  0x03,  0x03 },
	{  47,  31, 0x04,  0x7d,  0xdc, 0x0d,  0x1e,  0x09,  0x03,  0x03 },
	{  47,  31, 0x04,  0x55,  0xdc, 0x0d,  0x1e,  0x09,  0x03,  0x03 },
	{  50,  32, 0x04,  0x2e,  0xdc, 0x0d,  0x1e,  0x09,  0x03,  0x04 },
	{  50,  32, 0x04,  0x07,  0xdc, 0x0d,  0x1e,  0x0a,  0x03,  0x03 },
	{  53,  33, 0x03,  0xdf,  0xdc, 0x0d,  0x1e,  0x0a,  0x03,  0x03 },
	{  53,  33, 0x03,  0xb7,  0xdc, 0x0d,  0x1e,  0x0a,  0x03,  0x04 },
	{  56,  34, 0x03,  0x95,  0xdc, 0x0d,  0x1e,  0x0a,  0x04,  0x03 },
	{  56,  34, 0x03,  0x72,  0xdc, 0x0d,  0x1e,  0x0b,  0x03,  0x04 },
	{  60,  35, 0x03,  0x3c,  0xdc, 0x0d,  0x1e,  0x0b,  0x04,  0x03 },
	{  60,  35, 0x03,  0x06,  0xdc, 0x0d,  0x1e,  0x0b,  0x04,  0x04 },
	{  64,  36, 0x02,  0xcf,  0xdc, 0x0d,  0x1e,  0x0c,  0x04,  0x04 },
	{  64,  36, 0x02,  0x97,  0xdc, 0x0d,  0x1e,  0x0c,  0x04,  0x04 },
	{  68,  37, 0x03,  0x04,  0xdc, 0x0d,  0x1e,  0x0d,  0x04,  0x04 },
	{  68,  37, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x0d,  0x04,  0x05 },
	{  72,  38, 0x03,  0x01,  0xdc, 0x0d,  0x1e,  0x0d,  0x05,  0x04 },
	{  72,  38, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x0e,  0x04,  0x05 },
	{  77,  39, 0x03,  0x08,  0xdc, 0x0d,  0x1e,  0x0e,  0x05,  0x05 },
	{  77,  39, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x0f,  0x04,  0x05 },
	{  82,  40, 0x03,  0x05,  0xdc, 0x0d,  0x1e,  0x0f,  0x05,  0x05 },
	{  82,  40, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x10,  0x05,  0x05 },
	{  87,  41, 0x03,  0x02,  0xdc, 0x0d,  0x1e,  0x10,  0x05,  0x06 },
	{  87,  41, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x11,  0x05,  0x06 },
	{  93,  42, 0x03,  0x08,  0xdc, 0x0d,  0x1e,  0x11,  0x06,  0x06 },
	{  93,  42, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x12,  0x05,  0x06 },
	{  98,  43, 0x02,  0xfe,  0xdc, 0x0d,  0x1e,  0x12,  0x06,  0x06 },
	{  98,  43, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x13,  0x06,  0x06 },
	{ 105,  44, 0x03,  0x09,  0xdc, 0x0d,  0x1e,  0x13,  0x07,  0x06 },
	{ 105,  44, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x14,  0x07,  0x06 },
	{ 111,  45, 0x03,  0x00,  0xdc, 0x0d,  0x1e,  0x15,  0x06,  0x07 },
	{ 111,  45, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x15,  0x07,  0x07 },
	{ 119,  46, 0x03,  0x0a,  0xdc, 0x0d,  0x1e,  0x16,  0x07,  0x07 },
	{ 119,  46, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x17,  0x07,  0x08 },
	{ 126,  47, 0x03,  0x01,  0xdc, 0x0d,  0x1e,  0x17,  0x08,  0x08 },
	{ 126,  47, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x18,  0x08,  0x08 },
	{ 134,  48, 0x03,  0x04,  0xdc, 0x0d,  0x1e,  0x19,  0x08,  0x08 },
	{ 134,  48, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x1a,  0x08,  0x08 },
	{ 143,  49, 0x03,  0x07,  0xdc, 0x0d,  0x1e,  0x1a,  0x09,  0x09 },
	{ 143,  49, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x1b,  0x09,  0x09 },
	{ 152,  50, 0x03,  0x04,  0xdc, 0x0d,  0x1e,  0x1c,  0x09,  0x0a },
	{ 152,  50, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x1d,  0x09,  0x0a },
	{ 162,  51, 0x03,  0x06,  0xdc, 0x0d,  0x1e,  0x1e,  0x0a,  0x0a },
	{ 162,  51, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x1f,  0x0a,  0x0a },
	{ 172,  52, 0x03,  0x03,  0xdc, 0x0d,  0x1e,  0x20,  0x0a,  0x0b },
	{ 172,  52, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x21,  0x0a,  0x0b },
	{ 183,  53, 0x03,  0x04,  0xdc, 0x0d,  0x1e,  0x22,  0x0b,  0x0b },
	{ 183,  53, 0x02,  0xd7,  0xdc, 0x0d,  0x1e,  0x23,  0x0b,  0x0c },
	{ 195,  54, 0x02,  0xc9,  0xdc, 0x0d,  0x1e,  0x23,  0x0c,  0x0c },
	{ 195,  54, 0x02,  0xba,  0xdc, 0x0d,  0x1e,  0x23,  0x0c,  0x0c },
	{ 195,  54, 0x02,  0xac,  0xdc, 0x0d,  0x1e,  0x24,  0x0b,  0x0d },
	{ 195,  54, 0x02,  0x9e,  0xdc, 0x0d,  0x1e,  0x24,  0x0c,  0x0c },
	{ 195,  54, 0x02,  0x90,  0xdc, 0x0d,  0x1e,  0x25,  0x0b,  0x0d },
	{ 195,  54, 0x02,  0x81,  0xdc, 0x0d,  0x1e,  0x25,  0x0c,  0x0c },
	{ 195,  54, 0x02,  0x73,  0xdc, 0x0d,  0x1e,  0x25,  0x0c,  0x0d },
	{ 207,  55, 0x02,  0x63,  0xdc, 0x0d,  0x1e,  0x26,  0x0c,  0x0c },
	{ 207,  55, 0x02,  0x54,  0xdc, 0x0d,  0x1e,  0x26,  0x0c,  0x0d },
	{ 207,  55, 0x02,  0x44,  0xdc, 0x0d,  0x1e,  0x26,  0x0c,  0x0d },
	{ 207,  55, 0x02,  0x34,  0xdc, 0x0d,  0x1e,  0x26,  0x0d,  0x0d },
	{ 207,  55, 0x02,  0x24,  0xdc, 0x0d,  0x1e,  0x27,  0x0c,  0x0e },
	{ 207,  55, 0x02,  0x15,  0xdc, 0x0d,  0x1e,  0x27,  0x0d,  0x0d },
	{ 207,  55, 0x02,  0x05,  0xdc, 0x0d,  0x1e,  0x27,  0x0d,  0x0e },
	{ 220,  56, 0x01,  0xf2,  0xdc, 0x0d,  0x1e,  0x28,  0x0d,  0x0d },
	{ 220,  56, 0x01,  0xe0,  0xdc, 0x0d,  0x1e,  0x28,  0x0d,  0x0e },
	{ 220,  56, 0x01,  0xcd,  0xdc, 0x0d,  0x1e,  0x29,  0x0d,  0x0d },
	{ 220,  56, 0x01,  0xba,  0xdc, 0x0d,  0x1e,  0x29,  0x0d,  0x0e },
	{ 220,  56, 0x01,  0xa7,  0xdc, 0x0d,  0x1e,  0x29,  0x0e,  0x0d },
	{ 220,  56, 0x01,  0x95,  0xdc, 0x0d,  0x1e,  0x2a,  0x0d,  0x0e },
	{ 220,  56, 0x01,  0x82,  0xdc, 0x0d,  0x1e,  0x2a,  0x0e,  0x0e },
	{ 234,  57, 0x01,  0x72,  0xdc, 0x0c,  0x1e,  0x2a,  0x0e,  0x0e },
	{ 234,  57, 0x01,  0x62,  0xdc, 0x0c,  0x1e,  0x2b,  0x0e,  0x0e },
	{ 234,  57, 0x01,  0x52,  0xdc, 0x0c,  0x1e,  0x2b,  0x0e,  0x0e },
	{ 234,  57, 0x01,  0x42,  0xdc, 0x0c,  0x1e,  0x2b,  0x0e,  0x0f },
	{ 234,  57, 0x01,  0x32,  0xdc, 0x0c,  0x1e,  0x2c,  0x0e,  0x0f },
	{ 234,  57, 0x01,  0x22,  0xdc, 0x0c,  0x1e,  0x2c,  0x0e,  0x0f },
	{ 234,  57, 0x01,  0x12,  0xdc, 0x0c,  0x1e,  0x2c,  0x0f,  0x0e },
	{ 234,  57, 0x01,  0x02,  0xdc, 0x0c,  0x1e,  0x2d,  0x0e,  0x0f },
	{ 249,  58, 0x01,  0x4a,  0xdc, 0x0c,  0x1e,  0x2d,  0x0f,  0x0f },
	{ 249,  58, 0x01,  0x3b,  0xdc, 0x0c,  0x1e,  0x2d,  0x0f,  0x0f },
	{ 249,  58, 0x01,  0x2c,  0xdc, 0x0c,  0x1e,  0x2e,  0x0f,  0x0f },
	{ 249,  58, 0x01,  0x1d,  0xdc, 0x0c,  0x1e,  0x2e,  0x0f,  0x10 },
	{ 249,  58, 0x01,  0x0e,  0xdc, 0x0c,  0x1e,  0x2e,  0x0f,  0x10 },
	{ 249,  58, 0x00,  0xfe,  0xdc, 0x0c,  0x1e,  0x2f,  0x0f,  0x10 },
	{ 249,  58, 0x00,  0xef,  0xdc, 0x0c,  0x1e,  0x2f,  0x0f,  0x10 },
	{ 249,  58, 0x00,  0xe0,  0xdc, 0x0c,  0x1e,  0x2f,  0x10,  0x10 },
	{ 265,  59, 0x01,  0x4c,  0xdc, 0x0b,  0x1e,  0x30,  0x0f,  0x10 },
	{ 265,  59, 0x01,  0x3f,  0xdc, 0x0b,  0x1e,  0x30,  0x10,  0x10 },
	{ 265,  59, 0x01,  0x31,  0xdc, 0x0b,  0x1e,  0x30,  0x10,  0x10 },
	{ 265,  59, 0x01,  0x24,  0xdc, 0x0b,  0x1e,  0x31,  0x10,  0x10 },
	{ 265,  59, 0x01,  0x16,  0xdc, 0x0b,  0x1e,  0x31,  0x10,  0x11 },
	{ 265,  59, 0x01,  0x09,  0xdc, 0x0b,  0x1e,  0x32,  0x10,  0x10 },
	{ 265,  59, 0x00,  0xfb,  0xdc, 0x0b,  0x1e,  0x32,  0x10,  0x11 },
	{ 265,  59, 0x00,  0xee,  0xdc, 0x0b,  0x1e,  0x32,  0x10,  0x11 },
	{ 265,  59, 0x00,  0xe0,  0xdc, 0x0b,  0x1e,  0x32,  0x11,  0x11 },
	{ 282,  60, 0x01,  0x4c,  0xdc, 0x0a,  0x1e,  0x33,  0x10,  0x12 },
	{ 282,  60, 0x01,  0x3f,  0xdc, 0x0a,  0x1e,  0x33,  0x11,  0x11 },
	{ 282,  60, 0x01,  0x31,  0xdc, 0x0a,  0x1e,  0x34,  0x10,  0x12 },
	{ 282,  60, 0x01,  0x24,  0xdc, 0x0a,  0x1e,  0x34,  0x11,  0x11 },
	{ 282,  60, 0x01,  0x16,  0xdc, 0x0a,  0x1e,  0x34,  0x11,  0x12 },
	{ 282,  60, 0x01,  0x09,  0xdc, 0x0a,  0x1e,  0x35,  0x11,  0x11 },
	{ 282,  60, 0x00,  0xfb,  0xdc, 0x0a,  0x1e,  0x35,  0x11,  0x12 },
	{ 282,  60, 0x00,  0xee,  0xdc, 0x0a,  0x1e,  0x35,  0x12,  0x12 },
	{ 282,  60, 0x00,  0xe0,  0xdc, 0x0a,  0x1e,  0x36,  0x11,  0x12 },
	{ 300,  61, 0x01,  0x4d,  0xdc, 0x09,  0x1e,  0x36,  0x12,  0x12 },
	{ 300,  61, 0x01,  0x41,  0xdc, 0x09,  0x1e,  0x36,  0x12,  0x13 },
	{ 300,  61, 0x01,  0x35,  0xdc, 0x09,  0x1e,  0x37,  0x11,  0x13 },
	{ 300,  61, 0x01,  0x29,  0xdc, 0x09,  0x1e,  0x37,  0x12,  0x13 },
	{ 300,  61, 0x01,  0x1c,  0xdc, 0x09,  0x1e,  0x37,  0x12,  0x13 },
	{ 300,  61, 0x01,  0x10,  0xdc, 0x09,  0x1e,  0x38,  0x12,  0x13 },
	{ 300,  61, 0x01,  0x04,  0xdc, 0x09,  0x1e,  0x38,  0x12,  0x13 },
	{ 300,  61, 0x00,  0xf8,  0xdc, 0x09,  0x1e,  0x38,  0x13,  0x13 },
	{ 300,  61, 0x00,  0xec,  0xdc, 0x09,  0x1e,  0x39,  0x12,  0x13 },
	{ 300,  61, 0x00,  0xe0,  0xdc, 0x09,  0x1e,  0x39,  0x13,  0x13 },
	{ 316,  62, 0x01,  0x39,  0xdc, 0x09,  0x1e,  0x3a,  0x12,  0x14 },
	{ 316,  62, 0x01,  0x2d,  0xdc, 0x09,  0x1e,  0x3a,  0x13,  0x13 },
	{ 316,  62, 0x01,  0x20,  0xdc, 0x09,  0x1e,  0x3a,  0x13,  0x14 },
	{ 316,  62, 0x01,  0x13,  0xdc, 0x09,  0x1e,  0x3b,  0x13,  0x14 },
	{ 316,  62, 0x01,  0x06,  0xdc, 0x09,  0x1e,  0x3b,  0x13,  0x14 },
	{ 316,  62, 0x00,  0xfa,  0xdc, 0x09,  0x1e,  0x3b,  0x14,  0x14 },
	{ 316,  62, 0x00,  0xed,  0xdc, 0x09,  0x1e,  0x3c,  0x13,  0x14 },
	{ 316,  62, 0x00,  0xe0,  0xdc, 0x09,  0x1e,  0x3c,  0x14,  0x14 },
	{ 333,  63, 0x01,  0x3b,  0xdc, 0x08,  0x1e,  0x3d,  0x13,  0x15 },
	{ 333,  63, 0x01,  0x30,  0xdc, 0x08,  0x1e,  0x3d,  0x14,  0x14 },
	{ 333,  63, 0x01,  0x25,  0xdc, 0x08,  0x1e,  0x3d,  0x14,  0x15 },
	{ 333,  63, 0x01,  0x19,  0xdc, 0x08,  0x1e,  0x3e,  0x14,  0x15 },
	{ 333,  63, 0x01,  0x0e,  0xdc, 0x08,  0x1e,  0x3e,  0x14,  0x15 },
	{ 333,  63, 0x01,  0x02,  0xdc, 0x08,  0x1e,  0x3e,  0x15,  0x15 },
	{ 333,  63, 0x00,  0xf7,  0xdc, 0x08,  0x1e,  0x3f,  0x14,  0x15 },
	{ 333,  63, 0x00,  0xeb,  0xdc, 0x08,  0x1e,  0x3f,  0x15,  0x15 },
	{ 333,  63, 0x00,  0xe0,  0xdc, 0x08,  0x1e,  0x3f,  0x15,  0x15 },
	{ 350,  64, 0x01,  0x37,  0xdc, 0x07,  0x1e,  0x40,  0x15,  0x15 },
	{ 350,  64, 0x01,  0x2c,  0xdc, 0x07,  0x1e,  0x40,  0x15,  0x16 },
	{ 350,  64, 0x01,  0x21,  0xdc, 0x07,  0x1e,  0x41,  0x15,  0x15 },
	{ 350,  64, 0x01,  0x16,  0xdc, 0x07,  0x1e,  0x41,  0x15,  0x16 },
	{ 350,  64, 0x01,  0x0c,  0xdc, 0x07,  0x1e,  0x41,  0x15,  0x16 },
	{ 350,  64, 0x01,  0x01,  0xdc, 0x07,  0x1e,  0x42,  0x15,  0x16 },
	{ 350,  64, 0x00,  0xf6,  0xdc, 0x07,  0x1e,  0x42,  0x15,  0x17 },
	{ 350,  64, 0x00,  0xeb,  0xdc, 0x07,  0x1e,  0x42,  0x16,  0x16 },
	{ 350,  64, 0x00,  0xe0,  0xdc, 0x07,  0x1e,  0x43,  0x15,  0x17 },
	{ 357,  65, 0x00,  0xfe,  0xdc, 0x06,  0x1e,  0x43,  0x16,  0x16 },
	{ 357,  65, 0x00,  0xf4,  0xdc, 0x06,  0x1e,  0x43,  0x16,  0x17 },
	{ 357,  65, 0x00,  0xea,  0xdc, 0x06,  0x1e,  0x44,  0x16,  0x16 },
	{ 357,  65, 0x00,  0xe0,  0xdc, 0x06,  0x1e,  0x44,  0x16,  0x17 },
	{ 365,  66, 0x01,  0x01,  0xdc, 0x06,  0x1e,  0x44,  0x17,  0x17 },
	{ 365,  66, 0x00,  0xf6,  0xdc, 0x06,  0x1e,  0x45,  0x16,  0x17 },
	{ 365,  66, 0x00,  0xeb,  0xdc, 0x06,  0x1e,  0x45,  0x17,  0x17 },
	{ 365,  66, 0x00,  0xe0,  0xdc, 0x06,  0x1e,  0x46,  0x16,  0x18 },
	{ 372,  67, 0x00,  0xd0,  0xdc, 0x06,  0x1e,  0x46,  0x17,  0x17 },
	{ 372,  67, 0x00,  0xc1,  0xdc, 0x06,  0x1e,  0x46,  0x17,  0x18 },
	{ 372,  67, 0x00,  0xb1,  0xdc, 0x06,  0x1e,  0x47,  0x17,  0x18 },
	{ 380,  68, 0x00,  0xa6,  0xdc, 0x05,  0x1e,  0x47,  0x17,  0x18 },
	{ 380,  68, 0x00,  0x9a,  0xdc, 0x05,  0x1e,  0x48,  0x17,  0x18 },
	{ 380,  68, 0x00,  0x8e,  0xdc, 0x05,  0x1e,  0x48,  0x17,  0x19 },
	{ 380,  68, 0x00,  0x83,  0xdc, 0x05,  0x1e,  0x48,  0x18,  0x18 },
	{ 387,  69, 0x00,  0x77,  0xdc, 0x05,  0x1e,  0x49,  0x17,  0x19 },
	{ 387,  69, 0x00,  0x6b,  0xdc, 0x05,  0x1e,  0x49,  0x18,  0x19 },
	{ 387,  69, 0x00,  0x5f,  0xdc, 0x05,  0x1e,  0x49,  0x18,  0x19 },
	{ 387,  69, 0x00,  0x53,  0xdc, 0x05,  0x1e,  0x4a,  0x18,  0x19 },
	{ 395,  70, 0x00,  0x44,  0xdc, 0x05,  0x1e,  0x4a,  0x18,  0x19 },
	{ 395,  70, 0x00,  0x34,  0xdc, 0x05,  0x1e,  0x4a,  0x19,  0x19 },
	{ 395,  70, 0x00,  0x25,  0xdc, 0x05,  0x1e,  0x4b,  0x18,  0x19 },
	{ 395,  70, 0x00,  0x16,  0xdc, 0x05,  0x1e,  0x4b,  0x19,  0x19 },
	{ 403,  71, 0x00,  0x31,  0xdc, 0x05,  0x1e,  0x4c,  0x18,  0x1a },
	{ 403,  71, 0x00,  0x26,  0xdc, 0x05,  0x1e,  0x4c,  0x19,  0x19 },
	{ 403,  71, 0x00,  0x1b,  0xdc, 0x05,  0x1e,  0x4c,  0x19,  0x1a },
	{ 403,  71, 0x00,  0x10,  0xdc, 0x05,  0x1e,  0x4d,  0x19,  0x1a },
	{ 412,  72, 0x00,  0x34,  0xdc, 0x04,  0x1e,  0x4d,  0x19,  0x1a },
	{ 412,  72, 0x00,  0x28,  0xdc, 0x04,  0x1e,  0x4e,  0x19,  0x1a },
	{ 412,  72, 0x00,  0x1c,  0xdc, 0x04,  0x1e,  0x4e,  0x19,  0x1b },
	{ 412,  72, 0x00,  0x10,  0xdc, 0x04,  0x1e,  0x4e,  0x1a,  0x1a },
	{ 420,  73, 0x00,  0x30,  0xdc, 0x04,  0x1e,  0x4f,  0x19,  0x1b },
	{ 420,  73, 0x00,  0x25,  0xdc, 0x04,  0x1e,  0x4f,  0x1a,  0x1a },
	{ 420,  73, 0x00,  0x1b,  0xdc, 0x04,  0x1e,  0x4f,  0x1a,  0x1b },
	{ 420,  73, 0x00,  0x10,  0xdc, 0x04,  0x1e,  0x50,  0x1a,  0x1b },
};



static const int inflection_voltages[V_MAX] = {0, 1, 7, 11, 23, 35, 51, 87, 151, 203, 255};

/* fraction for gamma code */
static const int fraction[V_MAX][2] = {
	/* {offset, denominator} */
	{0,  860},	/* VT */
	{0,  256},	/* V1 */
	{64, 320},	/* V7 */
	{64, 320},	/* V11 */
	{64, 320},	/* V23 */
	{64, 320},	/* V35 */
	{64, 320},	/* V51 */
	{64, 320},	/* V87 */
	{64, 320},	/* V151 */
	{64, 320},	/* V203 */
	{129, 860},	/* V255 */
};

/**********************************************************************************************\
 * 1. VT       : VREG1 - (VREG1 -     0   ) * (OutputGamma + numerator_TP)/(denominator_TP)   *
 * 2. V255     : VREG1 - (VREG1 -     0   ) * (OutputGamma + numerator_TP)/(denominator_TP)   *
 * 3. VT1, VT7 : VREG1 - (VREG1 - V_nextTP) * (OutputGamma + numerator_TP)/(denominator_TP)   *
 * 4. other VT :    VT - (   VT - V_nextTP) * (OutputGamma + numerator_TP)/(denominator_TP)   *
\**********************************************************************************************/

static unsigned long long calc_gamma_voltage(int VT, int V_nextTP, int OutputGamma,
								const int fraction[])
{
	unsigned long long val1, val2, val3, val4;

	val1 = VT - V_nextTP;

	val2 = (unsigned long long)(fraction[0] + OutputGamma) << FP_SHIFT;
	do_div(val2, fraction[1]);
	val3 = (val1 * val2) >> FP_SHIFT;

	val4 = VT - val3;

	return val4;
}

/*
 *	each TP's gamma voltage calculation (2.3.3)
 */
static void TP_gamma_voltage_calc(int mtp[V_MAX], int rgb[V_MAX])
{
	int i;
	int OutputGamma;
	int MTP_OFFSET;
	int vt_coef;

	/* VT first */
	MTP_OFFSET = mtp[VT];

	switch(MTP_OFFSET) {
		case 0 ... 9: vt_coef = 12 * MTP_OFFSET ; break;
		case 10 ... 14: vt_coef = 10 * MTP_OFFSET + 38; break;
		case 15: vt_coef = 10 * MTP_OFFSET + 36; break;
	}

	rgb[VT] = calc_gamma_voltage(VREG0_REF_6P5, 0, vt_coef, fraction[VT]);

	/* V255 */
	MTP_OFFSET = mtp[V255];
	OutputGamma = MTP_OFFSET + 0x100; //center_gamma[V255];
	rgb[V255] = calc_gamma_voltage(VREG0_REF_6P5, 0, OutputGamma, fraction[V255]);

	/* V203 ~ V11 */
	for (i = V203; i >= V11; i--) {
		MTP_OFFSET = mtp[i];
		OutputGamma = MTP_OFFSET + 0x80;//center_gamma[i];
		rgb[i] = calc_gamma_voltage(rgb[VT], rgb[i + 1],
					OutputGamma, fraction[i]);
	}

	/* V7, V1*/
	for (i = V7; i >= V1; i--) {
		MTP_OFFSET = mtp[i];
		OutputGamma = MTP_OFFSET + 0x80;//center_gamma[i];
		rgb[i] = calc_gamma_voltage(VREG0_REF_6P5, rgb[i + 1],
					OutputGamma, fraction[i]);
	}
}



// 1. V255 : ((VREG1 - V255) * denominator_TP / vreg) - numerator_TP
static unsigned long long v255_TP_gamma_code_calc(int vreg, int grayscale, const int fraction[])
{
	unsigned long long val1, val2, val3;

	val1 = vreg - grayscale;
	val2 = val1 * fraction[1];
	do_div(val2, vreg);
	val3 = val2 - fraction[0];

	return val3;
}

// 2. other : (VT    - V_TP)* denominator_TP /(VT    - V_nextTP) - numerator_TP
// 3. V7,V1 : (VREG1 - V_TP)* denominator_TP /(VREG1 - V_nextTP) - numerator_TP
static unsigned long long other_TP_gamma_code_calc(int VT, int grayscale, int nextGRAY, const int fraction[])
{
	signed long long val1, val2, val3, val4;
	int gray_sign = 1, nextgray_sign = 1;

	if (unlikely((VT - grayscale) < 0))
		gray_sign = -1;

	if (unlikely((VT - nextGRAY) < 0))
		nextgray_sign = -1;

	val1 = VT - grayscale;
	val2 = val1 * fraction[1];
	val3 = VT - nextGRAY;
	val2 *= gray_sign;
	val3 *= nextgray_sign;
	do_div(val2, val3);
	val2 *= (gray_sign * nextgray_sign);
	val4 = val2 - fraction[0];
	return (unsigned long long) val4;
}

/* gray scale = V_down + (V_up - V_down) * num / den */
static int gray_scale_calc(int v_up, int v_down, int num, int den)
{
	unsigned long long val1, val2;

	val1 = v_up - v_down;

	val2 = (unsigned long long)(val1 * num) << FP_SHIFT;

	do_div(val2, den);

	val2 >>= FP_SHIFT;

	val2 += v_down;

	return (int)val2;
}

static int generate_gray_scale(int grayscale[GRAY_SCALE_MAX], int rgb[V_MAX])
{
	int i, V_idx, cal_cnt = 0;

	/* grayscale OUTPUT VOLTAGE of TP's (V1,V1,V7,V11,V23,V35,V51,V87,V151,V203,V255)
	   (V1 is VREG1) */

	grayscale[0] = VREG0_REF_6P5;
	for (i = 1; i < V_MAX; i++)
		grayscale[inflection_voltages[i]] = rgb[i];

	/* ALL grayscale OUTPUT VOLTAGE (0~255) */
	V_idx = V1; // start V of gray scale
	for (i = 0; i < GRAY_SCALE_MAX; i++) {
		if (i == inflection_voltages[V_idx]) {
			cal_cnt = 1;
			V_idx++;
		} else {
			int den = inflection_voltages[V_idx] - inflection_voltages[V_idx-1];
			grayscale[i] = gray_scale_calc(
				grayscale[inflection_voltages[V_idx-1]],
				grayscale[inflection_voltages[V_idx]],
				den - cal_cnt, den);
			cal_cnt++;
		}
	}

	return 0;
}

static int char_to_int(char data)
{
	return (data & 0x80) ? -((int) (data & 0x7f)) : (int) data;
}

enum { COMP_R, COMP_G, COMP_B, N_COMP };

static void s6e3fa7_gamma_cmd_add_component(struct s6e3fa7_drv *drv,
		int mtp[V_MAX], int rgb[V_MAX],
		int grayscale[GRAY_SCALE_MAX],
		int component, int table_index)
{
	int gamma[V_MAX] = { 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 256 };
	const unsigned char *M_GRAY;
	char *output;
	int i;

	table_index = clamp(table_index, 0, LUMINANCE_MAX - 1);
	M_GRAY = &gamma_offsets[table_index][0];

	output = drv->gamma_cmds[table_index];
	output[0] = 0xca;
	output++;

	if (table_index != LUMINANCE_MAX - 1) {
		const signed char *comp_offsets;
		int TP, nextTP;

		/* Generate gamma code */
		// V255
		TP = M_GRAY[V255];
		gamma[V255] = v255_TP_gamma_code_calc(VREG0_REF_6P5,
				grayscale[TP], fraction[V255]);

		// V203 ~ V11
		for (i = V203; i >= V11; i--) {
			TP = M_GRAY[i];
			nextTP = M_GRAY[i+1];
			gamma[i] = other_TP_gamma_code_calc(rgb[VT],
					grayscale[TP], grayscale[nextTP], fraction[i]);
		}

		// V7, V1
		for (i = V7; i >= V1; i--) {
			TP = M_GRAY[i];
			nextTP = M_GRAY[i+1];
			gamma[i] = other_TP_gamma_code_calc(VREG0_REF_6P5, grayscale[TP],
					grayscale[nextTP], fraction[i]);
		}

		/* Color Shift (RGB compensation) */
		switch (component) {
			case COMP_R: comp_offsets = red_offsets[table_index]; break;
			case COMP_G: comp_offsets = green_offsets[table_index]; break;
			case COMP_B: comp_offsets = blue_offsets[table_index]; break;
			default: break;
		}

		for (i = 0; i < RGB_COMPENSATION; i++)
			gamma[V255 - i] += comp_offsets[i];

		/* Subtract mtp offset from generated gamma table */
		gamma[V255] = clamp(gamma[V255] - mtp[V255], 0, 0xffff);
		for (i = V203; i >= V1; i--)
			gamma[i] = clamp(gamma[i] - mtp[i], 0, 0xff);
	}

	/* VT + V255 RGB */
	switch (component) {
	case COMP_R:
		output[0] |= (gamma[VT] & 0x0f) << 4;
		output[0] |= (gamma[V255] >> 6) & 4;
		output[2] = gamma[V255] & 0xff;
		break;
	case COMP_G:
		output[1] = (gamma[VT] & 0x0f) << 4;
		output[0] |= (gamma[V255] >> 7) & 2;
		output[3] = gamma[V255] & 0xff;
		break;
	case COMP_B:
		output[1] |= (gamma[VT] & 0x0f);
		output[0] |= (gamma[V255] >> 8) & 1;
		output[4] = 0;
		output[5] = gamma[V255] & 0xff;
		break;
	}

	/* V203 ~ v1 */
	for (i = V203; i >= V1; i--)
		output[6 + component + (V203 - i) * 3] = gamma[i];
}

static void s6e3fa7_gamma_init(struct s6e3fa7_drv *drv, u8 pfrom[32])
{
	int mtp_offsets[N_COMP][V_MAX];
	int grayscale[GRAY_SCALE_MAX];
	int rgb_out[N_COMP][V_MAX];
	int component, i, cnt;

	/* V255 */
	mtp_offsets[COMP_R][V255] = pfrom[2] * (pfrom[0] & 0x04 ? -1 : 1);
	mtp_offsets[COMP_G][V255] = pfrom[3] * (pfrom[0] & 0x02 ? -1 : 1);
	mtp_offsets[COMP_B][V255] = pfrom[4] * (pfrom[0] & 0x01 ? -1 : 1);

	/* V203 ~ V1 */
	cnt = 5;
	for (i = V203; i > VT; i--)
		for (component = 0; component < N_COMP; component ++)
			mtp_offsets[component][i] = char_to_int(pfrom[cnt++]);

	/* VT */
	mtp_offsets[COMP_R][VT] = (pfrom[0] >> 4) & 0xf;
	mtp_offsets[COMP_G][VT] = (pfrom[1] >> 4) & 0xf;
	mtp_offsets[COMP_B][VT] = pfrom[1] & 0x0f;

	/********************************************************************************************/
	/* Each TP's gamma voltage calculation							    */
	/* 1. VT, V255 : VREG1 - VREG1 * (OutputGamma + numerator_TP)/(denominator_TP)		    */
	/* 2. VT1, VT7 : VREG1 - (VREG1 - V_nextTP) * (OutputGamma + numerator_TP)/(denominator_TP) */
	/* 3. other VT : VT - (VT - V_nextTP) * (OutputGamma + numerator_TP)/(denominator_TP)	    */
	/********************************************************************************************/

	for (component = 0; component < N_COMP; component ++) {
		TP_gamma_voltage_calc(mtp_offsets[component], rgb_out[component]);
		generate_gray_scale(grayscale, rgb_out[component]);
		for (i = 0; i < LUMINANCE_MAX; i ++)
			s6e3fa7_gamma_cmd_add_component(drv, mtp_offsets[component],
					rgb_out[component], grayscale, component, i);
	}
}

static int s6e3fa7_read_eeprom(struct mipi_dsi_device *dsi,
				   u8 cmd, u8 offset, u8 *output, u8 len)
{
	int ret;
	u8 offset_cmd [] = { 0xb0, 0x00 };

	while (len > 0) {
		offset_cmd[1] = offset;

		ret = mipi_dsi_generic_write(dsi, offset_cmd, ARRAY_SIZE(offset_cmd));
		if (ret < 0)
			return ret;

		ret = mipi_dsi_dcs_read(dsi, cmd, output, len > 8 ? 8 : len);
		if (ret < 0)
			return ret;

		len -= ret;
		output += ret;
		offset += ret;
	}
	return 0;
}

static int s6e3fa7_get_brightness(struct backlight_device *bl)
{
	return bl->props.brightness;
}

static int s6e3fa7_update_status(struct backlight_device *bl)
{
	struct s6e3fa7_drv *drv = bl_get_data(bl);
	struct mipi_dsi_device *dsi = drv->dsi;
	const struct brightness_level *settings;
	int brightness = bl->props.brightness;
	u8 mipi_brightness = 0;
	u8 aid_cmd[] =  { 0xb1, 0x00, 0x10 };
	u8 elvss_cmd[] = {
		0xb5, 0x14, 0xcc, 0x0d, 0x01, 0x34, 0x67, 0x9a,
		0xcd, 0x01, 0x22, 0x33, 0x44, 0xc0, 0x00, 0x09,
		0x99, 0x33, 0x13, 0x01, 0x11, 0x10, 0x00, 0x00,
	};
	u8 vint_cmd[] = { 0xf4, 0xbb, 0x1e };
	u8 irc_cmd[] = { 0xb8, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
		0x80, 0x61, 0x2c, 0x30, 0x4e, 0xc4,
		0x33, 0x69, 0x12, 0x7a, 0xc7, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	};
	u8 *gamma_cmd;
	int ret, i;

	if (!drv->bl_inited) {
		u8 mtp_data[32];

		ret = mipi_dsi_generic_write(dsi, level1_on, ARRAY_SIZE(level1_on));
		if (ret < 0)
			return ret;

		/* Read ELVSS DATA (B5[22:23]) */
		ret = s6e3fa7_read_eeprom(dsi, 0xb5, 0x16,
				drv->elvss_params, ARRAY_SIZE(drv->elvss_params));
		if (ret < 0)
			return ret;

		/* Read IRC DATA (B8[0:1]) */
		ret = s6e3fa7_read_eeprom(dsi, 0xb8, 0,
				drv->irc_params, ARRAY_SIZE(drv->irc_params));
		if (ret < 0)
			return ret;

		/* Read MTP OFFSET (C8[0:31]) */
		ret = s6e3fa7_read_eeprom(dsi, 0xc8, 0,
				mtp_data, ARRAY_SIZE(mtp_data));
		if (ret < 0)
			return ret;

		ret = mipi_dsi_generic_write(dsi, level1_off, ARRAY_SIZE(level1_off));
		if (ret < 0)
			return ret;

		print_hex_dump(KERN_DEBUG, "MTP ", DUMP_PREFIX_OFFSET, 16, 1,
				mtp_data, 32, false);

		printk(KERN_DEBUG"ELVSS params: 0x%02x 0x%02x\n",
				drv->elvss_params[0], drv->elvss_params[1]);

		printk(KERN_DEBUG"IRC params: 0x%02x 0x%02x\n",
				drv->irc_params[0], drv->irc_params[1]);

		s6e3fa7_gamma_init(drv, mtp_data);
		drv->bl_inited = true;
	}

	if (bl->props.power != FB_BLANK_UNBLANK ||
	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) {
		return 0;
	}

	if (brightness == 0)
		brightness = 1;

	if (brightness < 0 || brightness > bl->props.max_brightness) {
		dev_err(&dsi->dev, "%s brightness out of range", __func__);
		return -EINVAL;
	}

	settings = &brightness_settings[brightness - 1];

	gamma_cmd = drv->gamma_cmds[clamp(settings->gamma_idx,
				     (u16) 0,  (u16) (LUMINANCE_MAX - 1))];
	elvss_cmd[23] = drv->elvss_params[0];
	irc_cmd[1] = drv->irc_params[0];
	irc_cmd[2] = drv->irc_params[1];
	aid_cmd[1] = clamp((u8)settings->aid1, (u8) 0,(u8)  8);
	aid_cmd[2] = settings->aid2;
	vint_cmd[2] = settings->vint2;
	elvss_cmd[2] = settings->elvss2;
	elvss_cmd[3] = settings->elvss3;
	for (i = 0; i < 3; i ++) {
		irc_cmd[25 + i] = settings->irc25_27;
		irc_cmd[28 + i] = settings->irc28_30;
		irc_cmd[31 + i] = settings->irc31_33;
	}

	ret = mipi_dsi_generic_write(dsi, level1_on, ARRAY_SIZE(level1_on));
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, aid_cmd, ARRAY_SIZE(aid_cmd));
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, elvss_cmd, ARRAY_SIZE(elvss_cmd));
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, vint_cmd, ARRAY_SIZE(vint_cmd));
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, irc_cmd, ARRAY_SIZE(irc_cmd));
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, gamma_cmd, GAMMA_CMD_LEN);
	if (ret < 0)
		goto fail;

	ret = mipi_dsi_generic_write(dsi, gamma_update_cmd, ARRAY_SIZE(gamma_update_cmd));
	if (ret < 0)
		goto fail;
fail:
	mipi_dsi_generic_write(dsi, level1_off, ARRAY_SIZE(level1_off));

	if (ret < 0) {
		dev_err(&dsi->dev, "%s mipi_dsi_generic_write failed: %d", __func__, ret);
		return ret;
	}

	mipi_brightness = min(brightness, 255);
	mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
			   &mipi_brightness, sizeof(mipi_brightness));

	return 0;
}

static int s6e3fa7_prepare(struct drm_panel *panel)
{
	struct s6e3fa7_drv *drv = to_s6e3fa7_drv(panel);
	struct mipi_dsi_device *dsi = drv->dsi;
	int ret;

	if (!drv->vregs_enabled) {
		ret = regulator_bulk_enable(NUM_VREGS, drv->vregs);
		if (ret) {
			dev_err(drv->dev, "Failed to enable regulators: %d\n", ret);
			return ret;
		}

		drv->vregs_enabled = true;
	}


	gpiod_set_value_cansleep(drv->reset, 0);
	usleep_range(10000, 11000);
	gpiod_set_value_cansleep(drv->reset, 1);
	usleep_range(5000, 6000);
	gpiod_set_value_cansleep(drv->reset, 0);
	usleep_range(10000, 11000);

	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(drv->dev, "Failed to exit sleep mode: %d\n", ret);
		return ret;
	}

	msleep(20);

	mipi_dsi_generic_write_seq(dsi, 0x35, 0x00);  // TE Vsync On
	mipi_dsi_generic_write_seq(dsi, LEVEL1_ON);
	mipi_dsi_generic_write_seq(dsi, 0xcc, 0x4c); // PCD Setting
	mipi_dsi_generic_write_seq(dsi, 0xed, 0x44); // ERR_FG
	mipi_dsi_generic_write_seq(dsi, 0xb9, 0x00, 0x00, 0x14, // TSP SYNC
			0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
			0x11, 0x01, 0x02, 0x40, 0x02, 0x40);
	mipi_dsi_generic_write_seq(dsi, 0xc5, 0x09, 0x10, 0xc8, // FFC SYNC
			0x21, 0x67, 0x11, 0x26, 0xd4);
	mipi_dsi_generic_write_seq(dsi, 0xf4, 0xbb, 0x1e, // AVC 2.0
			0x19, 0x3a, 0x9f, 0x0f, 0x09, 0xc0,
			0x00, 0xb4, 0x37, 0x70, 0x79, 0x69);
	mipi_dsi_generic_write_seq(dsi, 0xb0, 0x0e); // SAVE 5C enable
	mipi_dsi_generic_write_seq(dsi, 0xf2, 0x80);
	mipi_dsi_generic_write_seq(dsi, LEVEL1_OFF);

	s6e3fa7_update_status(drv->panel.backlight);

	usleep_range(10000, 11000);
	return 0;
}

static int s6e3fa7_enable(struct drm_panel *panel)
{
	struct s6e3fa7_drv *drv = to_s6e3fa7_drv(panel);
	int ret;

	ret = mipi_dsi_dcs_set_display_on(drv->dsi);
	if (ret < 0)
		dev_err(drv->dev, "Failed to set display on: %d\n", ret);

	return ret;
}

static int s6e3fa7_disable(struct drm_panel *panel)
{
	struct s6e3fa7_drv *drv = to_s6e3fa7_drv(panel);
	int ret;

	ret = mipi_dsi_dcs_set_display_off(drv->dsi);
	if (ret < 0)
		dev_err(drv->dev, "Failed to set display off: %d\n", ret);

	msleep(10);

	ret = mipi_dsi_dcs_enter_sleep_mode(drv->dsi);
	if (ret < 0)
		dev_err(drv->dev, "Failed to enter sleep mode: %d\n", ret);

	msleep(120);

	return 0;
}

static int s6e3fa7_unprepare(struct drm_panel *panel)
{
	struct s6e3fa7_drv *drv = to_s6e3fa7_drv(panel);
	int ret;

	gpiod_set_value_cansleep(drv->reset, 1);

	if (drv->vregs_enabled) {
		ret = regulator_bulk_disable(NUM_VREGS, drv->vregs);
		if (ret) {
			dev_err(drv->dev,
				"Failed to disable regulators: %d\n", ret);
			return ret;
		}

		drv->vregs_enabled = false;
	}

	return 0;
}

static const struct drm_display_mode s6e3fa7_mode_2220x1080 = {
	.clock = (1080 + 100 + 24 + 36) * (2220 + 4 + 2 + 14) * 60 / 1000,
	.hdisplay = 1080,
	.hsync_start = 1080 + 100,
	.hsync_end = 1080 + 100 + 24,
	.htotal = 1080 + 100 + 24 + 36,
	.vdisplay = 2220,
	.vsync_start = 2220 + 4,
	.vsync_end = 2220 + 4 + 2,
	.vtotal = 2220 + 4 + 2 + 14,
	.width_mm = 67,
	.height_mm = 138,
};

static int s6e3fa7_get_modes(struct drm_panel *panel,
		struct drm_connector *connector)
{
	return drm_connector_helper_get_modes_fixed(connector,
			&s6e3fa7_mode_2220x1080);
}

static const struct drm_panel_funcs s6e3fa7_panel_funcs = {
	.prepare = s6e3fa7_prepare,
	.enable = s6e3fa7_enable,
	.disable = s6e3fa7_disable,
	.unprepare = s6e3fa7_unprepare,
	.get_modes = s6e3fa7_get_modes
};
static const struct backlight_ops s6e3fa7_bl_ops = {
	.get_brightness = s6e3fa7_get_brightness,
	.update_status = s6e3fa7_update_status,
};

static const struct backlight_properties s6e3fa7_bl_props = {
	.max_brightness = ARRAY_SIZE(brightness_settings),
	.type = BACKLIGHT_RAW,
	.brightness = 100,
};

static int s6e3fa7_probe(struct mipi_dsi_device *dsi)
{
	struct backlight_device *bl_dev;
	struct device *dev = &dsi->dev;
	struct s6e3fa7_drv *drv;
	int ret;

	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
	if (!drv)
		return -ENOMEM;

	mipi_dsi_set_drvdata(dsi, drv);

	drv->dev = &dsi->dev;
	drv->dsi = dsi;
	drv->vregs[0].supply = "vdd";
	drv->vregs[1].supply = "vddr";
	drv->vregs[2].supply = "vci";
	dsi->lanes = 4;
	dsi->format = MIPI_DSI_FMT_RGB888;
	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
			  MIPI_DSI_MODE_LPM |
			  MIPI_DSI_CLOCK_NON_CONTINUOUS;

	ret = devm_regulator_bulk_get(dev, NUM_VREGS, drv->vregs);
	if (ret)
		return ret;

	drv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
	if (IS_ERR(drv->reset))
		return dev_err_probe(dev, PTR_ERR(drv->reset),
				"Failed to get reset-gpios: %d\n", ret);

	drm_panel_init(&drv->panel, dev, &s6e3fa7_panel_funcs,
			DRM_MODE_CONNECTOR_DSI);

	drv->panel.prepare_prev_first = true;

	bl_dev = devm_backlight_device_register(dev, dev_name(dev), dev, drv,
			&s6e3fa7_bl_ops, &s6e3fa7_bl_props);
	if (IS_ERR_OR_NULL(bl_dev))
		return dev_err_probe(dev, PTR_ERR(bl_dev) ?: -ENODATA,
				"Failed to register backlight device");

	drv->panel.backlight = bl_dev;
	drm_panel_add(&drv->panel);

	ret = mipi_dsi_attach(dsi);
	if (ret < 0)
		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");

	return 0;
}

static void s6e3fa7_remove(struct mipi_dsi_device *dsi)
{
	struct s6e3fa7_drv *drv = mipi_dsi_get_drvdata(dsi);
	int ret;

	ret = mipi_dsi_detach(dsi);
	if (ret < 0)
		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);

	drm_panel_remove(&drv->panel);
}

static const struct of_device_id s6e3fa7_of_match[] = {
	{ .compatible = "samsung,s6e3fa7-ams604nl01" },
	{ }
};

static struct mipi_dsi_driver s6e3fa7_driver = {
	.probe = s6e3fa7_probe,
	.remove = s6e3fa7_remove,
	.driver = {
		.name = "panel-s6e3fa7",
		.of_match_table = s6e3fa7_of_match,
	},
};
module_mipi_dsi_driver(s6e3fa7_driver)

MODULE_DEVICE_TABLE(of, s6e3fa7_of_match);
MODULE_DESCRIPTION("DRM driver for Samsung S6E3FA7");
MODULE_LICENSE("GPL v2");
